﻿// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME	    : FI.Net Application Framework
// COMPONENT ID	    : FI.Core
// COMPONENT DESC   : The foundation component of the whole framework. 	
//
// CREATED DATE/BY  : 30 Nov 2011 / Tian Mi
//
// REVISION HISTORY :	
// DATE/BY			SR#/CS/PM#/OTHERS		DESCRIPTION OF CHANGE
// ==================================================================================================
using System;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.Unity.Utility;

namespace FI.Core.IoC
{
    /// <summary>
    /// This <see cref="BuilderStrategy"/> is used to automatically register interceptor.
    /// </summary>
    public  class AutoInterceptionStrategy: BuilderStrategy
    {
        /// <summary>
        /// Pres the build up.
        /// </summary>
        /// <param name="context">The <see cref="IBuilderContext"/>.</param>
        public override void PreBuildUp(IBuilderContext context)
        {
            Guard.ArgumentNotNull(context, "context");
            if (!this.CanIntercept(context))
            {
                return;
            }
            IInstanceInterceptionPolicy interceptionPolicy = FindInterceptionPolicy<IInstanceInterceptionPolicy>(context, true);
            if (null == interceptionPolicy)
            {
                AutoInterceptorPolicy autoInterceptorPolicy = context.NewBuildUp<AutoInterceptorPolicy>(typeof(AutoInterceptorPolicy).AssemblyQualifiedName);
                if (null == autoInterceptorPolicy)
                {
                    return;
                }
                IInstanceInterceptor interceptor = autoInterceptorPolicy.Interceptor as IInstanceInterceptor;
                if (null == interceptor)
                {
                    return;
                }
                if (!interceptor.CanIntercept(context.BuildKey.Type))
                {
                    return;
                }
                context.Policies.Set<IInstanceInterceptionPolicy>(new FixedInstanceInterceptionPolicy(interceptor), context.BuildKey);
                context.Policies.Clear<ITypeInterceptionPolicy>(context.BuildKey);
            }

            IInterceptionBehaviorsPolicy interceptionBehaviorsPolicy = FindInterceptionPolicy<IInterceptionBehaviorsPolicy>(context, true);
            if (null == interceptionBehaviorsPolicy)
            {
                var policyInjectionBehavior = new InterceptionBehavior<PolicyInjectionBehavior>();
                policyInjectionBehavior.AddPolicies(context.OriginalBuildKey.Type, context.BuildKey.Type, context.BuildKey.Name, context.Policies);
            }
        }
       
        /// <summary>
        /// Determines whether this instance can intercept the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        ///   <c>true</c> if this instance can intercept the specified context; otherwise, <c>false</c>.
        /// </returns>
        protected bool CanIntercept(IBuilderContext context)
        {
            Guard.ArgumentNotNull(context, "context");
            if (context.BuildKey.Type.IsTypeOfUnity())
            {
                return false;
            }
            if (context.BuildKey.Type.IsTypeOfEntLib())
            {
                return false;
            }

            if (typeof(AutoInterceptorPolicy).IsAssignableFrom(context.BuildKey.Type))
            {
                return false;
            }

            return true;
        }

        private static T FindInterceptionPolicy<T>(IBuilderContext context, bool probeOriginalKey)
           where T : class, IBuilderPolicy
        {
            T policy;
            // First, try for a match against the current build key
            Type currentType = context.BuildKey.Type;
            policy = context.Policies.Get<T>(context.BuildKey, false) ?? context.Policies.Get<T>(currentType, false);
            if (policy != null)
            {
                return policy;
            }

            if (!probeOriginalKey)
            {
                return null;
            }

            // Next, try the original build key
            Type originalType = context.OriginalBuildKey.Type;
            policy = context.Policies.Get<T>(context.OriginalBuildKey, false) ?? context.Policies.Get<T>(originalType, false);

            return policy;
        }
     }
}
